------------------------------------------------------------------------------------
-- base/decider.lua
--
-- 本模块是行为决策管理模块，运行时行为管理、函数封装器、公共函数封装等。
--
-- @module      decider
-- @author      admin
-- @license     MIT
-- @release     v1.0.0 - 2023-03-22
-- @copyright   2023
-- @usage
-- 本模块不能被 main_test 入口函数调用，只能在正式模块中使用。
------------------------------------------------------------------------------------

-- 模块定义
---@class decider
local decider = {
    -- 模块版本 (主版本.次版本.修订版本)
    VERSION                  = '1.0.0',
    -- 作者备注 (更新日期 - 更新内容简述)
    AUTHOR_NOTE              = '2023-03-22 - Initial release',
    -- 模块名称
    MODULE_NAME              = 'decider module',
    -- 只读模式
    READ_ONLY                = false,

    -- 包装函数列表
    wrapped_funcs            = {},
    -- 包装函数列表
    wrapped_info_funcs       = {},
    -- 堆栈函数列表
    co_stack_funcs           = {},
    -- 执行缓存列表
    function_execution_cache = {},
    -- 模块列表
    module_list              = {},

    -- 全局监控运行标识
    is_global_monitor_active = false,
    -- 当前模块监控运行标识
    is_local_monitor_active  = false,

    -- 强制暂停切换模块 当在执行功能时 防止模块切换打断动作
    is_pause_change          = false,

}

-- 实例对象
local this = decider
-- 设置模块
local settings = settings
-- 日志模块
local trace = trace
-- 优化列表
local sleep = sleep
local is_working = is_working
local os = os
local coroutine = coroutine
local table = table
local ipairs = ipairs
local tostring = tostring
local pairs = pairs
local setmetatable = setmetatable
local assert = assert
local rawset = rawset
local error = error
local get_counter_value = get_counter_value
local increment_counter = increment_counter
local clear_counter = clear_counter
-- 内部函数
local xxhash = xxhash
local get_run_signal = get_run_signal
local game_unit = game_unit
local get_game_status = game_unit.get_game_status_ex
local is_key_down = main_unit.is_key_down2

-------------------------------------------------------------------------------------
-- 设置暂停模块切换
-- 这个函数用于暂停模块切换
-------------------------------------------------------------------------------------
function decider.set_pause_change(value)
    if this.current_context then
        -- 将传入的监控模块配置为当前决策器使用的监控模块
        this.current_context.is_pause_change = value
    end
end

-------------------------------------------------------------------------------------
-- 读取暂停模块切换的设置
-- 这个函数用于读取暂停模块切换的设置
-------------------------------------------------------------------------------------
function decider.get_pause_change(context)
    -- 将传入的监控模块配置为当前决策器使用的监控模块
    --if this.current_context == context then
    --	return context.is_pause_change
    --end
    return context.is_pause_change
end

-------------------------------------------------------------------------------------
-- 设置上下文
--
-- @local
-------------------------------------------------------------------------------------
function decider.set_curr_context(context)
    -- 设置当前模块
    this.current_context = context
    if context then
        -- 清零进入时间
        context.entry_time = 0
        -- 重置超时标识
        context.timeout_state = false
        -- 未开启暂停模块切换
        context.is_pause_change = false
    end
end

-------------------------------------------------------------------------------------
-- 设置监控模块
-- 这个函数用于配置或初始化全局监控模块。
-------------------------------------------------------------------------------------
function decider.set_global_monitor(monitor)
    -- 将传入的监控模块配置为当前决策器使用的监控模块
    this.current_monitor = monitor
end

-------------------------------------------------------------------------------------
-- 设置模块列表
-- 这个函数用于配置或初始化模块列表。
-------------------------------------------------------------------------------------
function decider.set_module_list(module_list)
    -- 设置模块列表
    this.module_list = module_list
end

-------------------------------------------------------------------------------------
-- 设置超时
--
-- @local
-------------------------------------------------------------------------------------
function decider.clear_time_out(set_time)
    local context = this.current_context
    if context then
        -- 设置进入时间为当前时
        context.entry_time        = os.time()
        context.eval_ifs.time_out = set_time
        this.current_context      = context
    end
end

-------------------------------------------------------------------------------------
-- 切片延时
--
-- @tparam 		 integer   ms 			    要休眠的毫秒数
-- @tparam[opt]  number    max_ms		    最大时间值
-- @tparam[opt]  number	   slice 		    切片大小，用于分割延时时间，默认为 500 毫秒
-- @return boolean 延时成功，则返回 true；遇到中断或参数无效，则返回 false。
-- @usage decider.sleep(1000)
-------------------------------------------------------------------------------------
function decider.sleep_ex(ms, max_ms, slice)
    -- 验证输入参数，如果延迟时间为0，则直接返回，不执行延迟
    if ms == 0 then
        return false
    end

    -- 如果提供了最大毫秒数并且大于基础毫秒数，随机选择一个延迟时间
    if max_ms and max_ms > ms then
        ms = math.random(ms, max_ms)
    end

    -- 设置分割时间片的大小，默认为500毫秒
    slice = slice or 500

    -- 计算总的时间片数量和最后一个时间片的剩余时间
    local n = ms // slice
    local mod = ms % slice

    -- 辅助函数用于检查工作状态并执行延迟
    local function try_sleep(duration)
        if this.is_working(true) then
            sleep(duration)
        else
            return false
        end
        return true
    end

    -- 循环执行分割的延迟
    for i = 1, n do
        if not try_sleep(slice) then return false end
    end

    -- 如果有剩余时间，执行最后一片的延迟
    if mod > 0 and not try_sleep(mod) then
        return false
    end

    -- 正常完成所有延迟后返回true，表示未中断
    return true
end

-------------------------------------------------------------------------------------
-- 切片延时
--
-- @tparam 		 integer   ms 			    要休眠的毫秒数
-- @tparam[opt]	 number    max_ms		    最大时间值
-- @tparam[opt]  number	   slice 		    切片大小，用于分割延时时间，默认为 500 毫秒
-- @usage decider.sleep(1000)
-------------------------------------------------------------------------------------
function decider.sleep(ms, max_ms, slice)
    return this.sleep_ex(ms, max_ms, slice)
end

-------------------------------------------------------------------------------------
-- 检查给定上下文的前置条件是否满足。
-- 此函数通过评估配置的游戏状态、配置项以及工作状态来确定是否继续执行。
-- @local
-- @param context table 包含条件和配置的上下文对象。
-- @return boolean 返回 true 如果所有条件满足，否则返回 false。
-------------------------------------------------------------------------------------
function decider.verify_context_eval_ifs(context)
    -- 检查系统中断标志
    if not is_working() then
        return decider.error_exit('系统中断标志')
    end

    -- 本地化使用
    local eval_ifs   = context.eval_ifs

    -- 获取游戏状态
    local curr_state = get_game_status()

    -- 效验 [启用] 游戏状态列表
    local check_ok   = false
    if #eval_ifs.yes_game_state > 0 then
        for k, v in ipairs(eval_ifs.yes_game_state)
        do
            if v & curr_state ~= 0 then
                check_ok = true
                break
            end
        end
        if not check_ok then return false end
    end

    -- 效验 [禁用] 游戏状态列表
    check_ok = true
    for k, v in ipairs(eval_ifs.not_game_state)
    do
        --if v & curr_state ~= 0 then
        if v == curr_state then
            check_ok = false
            break
        end
    end

    if not check_ok then return false end
    -- 检查必须启用的配置项
    for k, v in ipairs(eval_ifs.yes_config) do
        if not ini_ctx:get_bool(v) then
            return decider.error_exit('配置项启用 但未达成设置')
        end
    end

    -- 检查必须禁用的配置项
    for k, v in ipairs(eval_ifs.not_config) do
        if ini_ctx:get_bool(v) then
            return decider.error_exit('配置项禁用 但未达成设置')
        end
    end

    -- 检测前置工作状态
    if type(eval_ifs.is_pri_working) == 'function' then
        -- 前置工作条件校验
        if not eval_ifs.is_pri_working() then
            return decider.error_exit('前置工作条件校验 未达成设置')
        end
        -- 强制执行功能后再退出
        if this.get_pause_change(context) then
            return decider.success_exit('强制执行功能')
        end
    end

    -- 标记是否执行脚本
    local can_working = not eval_ifs.is_working
    local msg         = ''

    -- 不能工作时返回终止日志
    if not can_working then
        can_working, msg = decider.cached_function_call(eval_ifs.is_working, 1) --eval_ifs.is_working()
    end

    -- 检查特殊工作条件
    return can_working, msg
end

----------------------------------------------------------------------------------------
-- 在模块列表中寻找最高优先级的模块。
--
-- 该函数遍历一个模块列表，筛选出满足其 eval_ifs 中定义的工作条件的模块。
-- 优先处理那些已经开始且不可中断的模块。如果存在这样的模块，只返回包含该模块的列表。
-- 否则，按优先级对其余模块进行排序后返回排序列表。
--
-- @local
-- @param module_list table 待评估的模块列表。
-- @return table 返回按最高优先级排序的模块列表，或者如果存在不可中断且已开始的模块，则只返回该模块。
-------------------------------------------------------------------------------------
function decider.find_highest_priority_modules(module_list)
    -- 如果没有提供 module_list，则默认使用 this.module_list
    module_list                   = module_list or this.module_list

    local highest_priority_module = nil
    local eligible_modules        = {}
    local no_run_module           = {}
    -- 遍历模块列表，选择符合条件的模块
    for _, module in ipairs(module_list) do
        local bool, msg = this.verify_context_eval_ifs(module)
        if bool then
            -- 缓存 eval_ifs 对象
            local eval_ifs        = module.eval_ifs
            -- 检查模块是否已经开始
            local started         = eval_ifs.is_started()
            -- 检查模块是否可中断
            local can_interrupt   = eval_ifs.can_interrupt()
            -- 优先处理当前不可暂停的模块
            local is_pause_change = module.is_pause_change

            -- 存在暂不退出的模块 将优先级设置最高
            if not highest_priority_module and is_pause_change then
                highest_priority_module = module
            end

            -- 优先处理不可中断且已经开始的模块
            if started and not can_interrupt and not is_pause_change then
                if not highest_priority_module or eval_ifs.priority() < highest_priority_module.eval_ifs.priority() then
                    highest_priority_module = module
                end
            else
                -- 其他符合工作条件的模块
                table.insert(eligible_modules, module)
            end
        else
            module.stop_msg = msg
            table.insert(no_run_module, module)
        end
    end

    -- 对其他符合条件的模块按优先级排序
    table.sort(no_run_module, function(a, b)
        return a.eval_ifs.priority() < b.eval_ifs.priority()
    end)

    -- 如果存在一个不可中断且已开始的最高优先级模块，只返回该模块
    if highest_priority_module then
        return { highest_priority_module }, no_run_module
    end

    -- 对其他符合条件的模块按优先级排序
    table.sort(eligible_modules, function(a, b)
        return a.eval_ifs.priority() < b.eval_ifs.priority()
    end)

    return eligible_modules, no_run_module
end

-------------------------------------------------------------------------------------
-- 输出模块未执行原因
function decider.put_no_module_msg(no_run_module)
    if not table.is_empty(no_run_module) then
        local first_show = true
        trace.output('无可以执行的模块')
        -- 【副本模块】 未执行原因：前置工作条件校验 未达成设置
        for _, module in ipairs(no_run_module) do
            if module.stop_msg and not string.find(module.stop_msg, '未达成设置') then
                if first_show then
                    trace.log_error('**********************************************************')
                    first_show = false
                end
                trace.log_warn('【' .. module.MODULE_NAME .. '】 未执行原因：' .. module.stop_msg)
            end
        end
    end
end

-------------------------------------------------------------------------------------
-- 检查工作状态是否有效。
--
-- @param skip_monitoring boolean  (optional) 是否跳过监测，默认为false。如果为true，函数将直接返回true。
-- @treturn boolean               如果工作状态有效，返回true；否则返回false。
--
-- @usage
-- -- 持续检查工作状态，直到无效为止
-- while decider.is_working() do
--     decider.sleep(2500)
-- end
-------------------------------------------------------------------------------------
function decider.is_working(skip_monitoring)
    -- 系统中断标志
    if not is_working() then
        return false
    end

    -- 执行调度轮循
    this.perform_scheduled_monitor()

    -- 效验函数中断
    if this.interrupt_guard() then
        return false
    end

    -- 本地化使用
    local context = this.current_context
    local eval_ifs = context.eval_ifs

    -- 效验退出标识
    if context.should_exit then
        return false
    end

    -- 效验超时
    if eval_ifs.time_out and eval_ifs.time_out ~= 0 then
        local entry_time = context.entry_time
        if entry_time ~= 0 then
            if os.time() - entry_time > eval_ifs.time_out then
                context.timeout_state = true
                return false
            end
        end
    end

    -- 其它条件
    local result = this.verify_context_eval_ifs(context)
    if result and not skip_monitoring then
        -- 执行当前模块监控函数
        if this.execute_local_monitor() then
            result = false -- 标识二次效验
        end

        -- 执行全局监控函数
        if this.execute_global_monitor() then
            result = false -- 标识二次效验
        end

        -- 检测中断标志(这里还需要优化)
        if not result then
            result = this.verify_context_eval_ifs(context)
        end
    end

    return result
end

-------------------------------------------------------------------------------------
-- 将秒数格式化为 "X时X分X秒" 的格式。
--
-- @param seconds number 要转换的总秒数。
-- @return string 返回格式化后的字符串，描述相应的小时、分钟和秒数。
-------------------------------------------------------------------------------------
local function format_time(seconds)
    -- 计算小时数
    local hours = math.floor(seconds / 3600)
    -- 计算剩余的分钟数
    local minutes = math.floor((seconds % 3600) / 60)
    -- 计算剩余的秒数
    local remaining_seconds = seconds % 60

    -- 组装成 "X时X分X秒" 的格式
    return string.format("%d时%02d分%02d秒", hours, minutes, remaining_seconds)
end

-------------------------------------------------------------------------------------
-- 打印模块信息
--
-- @local
-------------------------------------------------------------------------------------
function decider.print_module_info()
    for _, module in ipairs(this.module_list) do
        -- 模块信息
        local should_exit = module.should_exit
        local entry_count = module.entry_count
        local entry_time = module.entry_time
        local leave_time = module.leave_time
        local last_occupy_duration = module.last_occupy_duration
        local total_occupy_duration = module.total_occupy_duration
        local status_message = should_exit and "未激活" or "已激活"
        xxxmsg(3, string.format('%s(%s)', tostring(module), status_message))
        xxxmsg(2, string.format('	%-32s: {%s}', 'should_exit', should_exit))
        xxxmsg(2, string.format('	%-32s: {%s}', 'entry_count', entry_count))
        xxxmsg(2, string.format('	%-32s: {%s}', 'entry_time', os.date("%Y-%m-%d %H:%M:%S", entry_time)))
        xxxmsg(2, string.format('	%-32s: {%s}', 'leave_time', os.date("%Y-%m-%d %H:%M:%S", leave_time)))
        if not should_exit then
            last_occupy_duration = os.time() - entry_time
            total_occupy_duration = total_occupy_duration + last_occupy_duration
        end
        xxxmsg(2, string.format('	%-32s: {%s}', 'last_occupy_duration', format_time(last_occupy_duration)))
        xxxmsg(2, string.format('	%-32s: {%s}', 'total_occupy_duration', format_time(total_occupy_duration)))
        -- 模块eval_ifs信息
        local eval_ifs = module.eval_ifs
        if eval_ifs then
            -- 使用 string.format 格式化输出 eval_ifs 的配置信息
            xxxmsg(2,
                string.format('	%-32s: {%s}', 'eval_ifs.yes_game_state',
                    trace.concat(eval_ifs.yes_game_state, ', ', true)))
            xxxmsg(2,
                string.format('	%-32s: {%s}', 'eval_ifs.not_game_state',
                    trace.concat(eval_ifs.not_game_state, ', ', true)))
            xxxmsg(2, string.format('	%-32s: {%s}', 'eval_ifs.yes_config', trace.concat(eval_ifs.yes_config, ', ', true)))
            xxxmsg(2, string.format('	%-32s: {%s}', 'eval_ifs.not_config', trace.concat(eval_ifs.not_config, ', ', true)))
            xxxmsg(2, string.format('	%-32s: {%d}', 'eval_ifs.time_out', eval_ifs.time_out))

            -- 打印动态函数的返回值
            xxxmsg(2, string.format('	%-32s: {%d}', 'eval_ifs.priority', eval_ifs.priority()))
            xxxmsg(2, string.format('	%-32s: {%s}', 'eval_ifs.is_working', tostring(eval_ifs.is_working())))
            xxxmsg(2, string.format('	%-32s: {%s}', 'eval_ifs.is_execute', tostring(eval_ifs.is_execute())))
            xxxmsg(2, string.format('	%-32s: {%s}', 'eval_ifs.can_interrupt', tostring(eval_ifs.can_interrupt())))
            xxxmsg(2, string.format('	%-32s: {%s}', 'eval_ifs.is_started', tostring(eval_ifs.is_started())))
        end
    end
end

-------------------------------------------------------------------------------------
-- 打印行为函数信息
--
-- @local
-------------------------------------------------------------------------------------
function decider.print_action_function_info()
    for _, func in pairs(this.wrapped_info_funcs) do
        if func.call_count > 0 or func.fail_count > 0 then
            xxxmsg(3, string.format('%s', func.action_name))
            xxxmsg(2, string.format('	%-32s: {%s}', 'func_type', func.func_type))
            xxxmsg(2, string.format('	%-32s: {%s}', 'cond_func', func.cond_func))
            xxxmsg(2, string.format('	%-32s: {%s}', 'timeout', func.timeout))
            xxxmsg(2, string.format('	%-32s: {%s}', 'start_time', os.date("%Y-%m-%d %H:%M:%S", func.start_time)))
            xxxmsg(2, string.format('	%-32s: {%ss}', 'last_occupy_duration', func.last_occupy_duration))
            xxxmsg(2, string.format('	%-32s: {%s}', 'call_count', func.call_count))
            xxxmsg(2, string.format('	%-32s: {%s}', 'fail_count', func.fail_count))
        end
    end
end

-------------------------------------------------------------------------------------
-- 执行计划的监控任务。
--
-- 该函数首先检查是否到达执行监控的预定时间间隔，如果未到时间则直接返回。
-- 如果当前没有任何模块在运行，同样返回不执行后续代码。
-- 否则，从可用模块中找出最高优先级的模块，检查当前运行的上下文是否需要切换。
-- 如果当前上下文不是最高优先级模块，则标记当前上下文为应退出状态。
-- 如果没有可执行的模块，显示错误信息。
--
-- 功能：
-- 1. 检测是否到达下一次执行监控的时间。
-- 2. 检查是否有模块需要执行，并进行相应的上下文切换。
-- 3. 如果当前上下文已不是最优先，则准备切换到更高优先级的模块。
-- 4. 如果无模块可执行，通知用户。
--
-- 使用全局变量 this.current_context 作为当前执行模块的上下文。
-- 使用全局设置 settings.scheduled_monitor_interval 确定监控任务的执行间隔。
-- @local
-------------------------------------------------------------------------------------
function decider.perform_scheduled_monitor()
    -- 检测运行信号，确定是否到达监控的预定时间
    if not get_run_signal(this.perform_scheduled_monitor, settings.scheduled_monitor_interval * 1000, 1) then
        return
    end

    -- 如果当前没有模块在执行，直接返回
    if not this.current_context then
        return
    end

    -- 打印模块信息(按住左CTRL不放)
    if is_key_down(0xA2) then
        xpcall(this.print_module_info, this.error_handler)
    end

    -- 打印函数信息(按住右CTRL不放)
    if is_key_down(0xA3) then
        xpcall(this.print_action_function_info, this.error_handler)
    end

    -- 获取优先级最高的模块列表
    local modules, no_run_module = this.find_highest_priority_modules()
    if #modules > 0 then
        local module = modules[1] -- 获取最高优先级的模块
        -- xprint('perform_scheduled_monitor', #modules, this.current_context, module)

        -- 检查当前上下文是否需要切换到新的模块
        if this.current_context ~= module then
            -- 标记当前上下文应当退出
            this.current_context.should_exit = true
            trace.log_warn(string.format('模块切换: {%s} → {%s}', this.current_context, module))
        end
    else
        -- 如果没有可执行的模块，输出错误信息
        decider.put_no_module_msg(no_run_module)
    end
end

-------------------------------------------------------------------------------------
-- 执行本地监控函数
--
-- @local
-- @return boolean 返回 true 表示监控逻辑已执行且。
-- @usage decider.execute_local_monitor()
-------------------------------------------------------------------------------------
function decider.execute_local_monitor()
    -- 执行当前模块监控函数
    if this.is_local_monitor_active then
        return false
    end

    -- 检测运行信号
    if get_run_signal(this.inside_looping, settings.local_monitor_interval * 1000, 1) then
        this.is_local_monitor_active = true
        this.inside_looping() -- 执行监控函数
        this.is_local_monitor_active = false
        return true     -- 标识二次效验
    end

    return false
end

-------------------------------------------------------------------------------------
-- 执行全局监控函数
--
-- @local
-- @return boolean 返回 true 表示监控逻辑已执行且。
-- @usage decider.execute_global_monitor()
-------------------------------------------------------------------------------------
function decider.execute_global_monitor()
    -- 效验是否设置了全局监控
    if not this.current_monitor then
        return false
    end

    -- 执行全局监控函数
    if get_run_signal(this.current_monitor.looping, settings.global_monitor_interval * 1000, 1) then
        if not this.is_global_monitor_active then
            this.is_global_monitor_active = true
            xpcall(this.current_monitor.looping, this.error_handler, this.current_context)
            this.is_global_monitor_active = false
            return true -- 标识二次效验
        end
    end

    return false -- 如果没有执行，返回 false 表示无变化
end

-------------------------------------------------------------------------------------
-- 执行功能函数前置效验
--
-- @local
-------------------------------------------------------------------------------------
function decider.should_execute()
    -- 直接从当前上下文获取执行条件
    local eval_ifs = this.current_context.eval_ifs

    -- 如果存在is_execute函数，直接返回其结果，否则默认为true
    return eval_ifs.is_execute and eval_ifs.is_execute() or true
end

-------------------------------------------------------------------------------------
-- 效验函数中断
--
-- @local
-------------------------------------------------------------------------------------
function decider.interrupt_guard()
    -- 获取函数对象
    local id = coroutine.running()
    local stack_funcs = this.co_stack_funcs[id]
    if stack_funcs == nil or #stack_funcs == 0 then
        return false
    end
    -- 效验函数是否设置超时
    local fn_info = stack_funcs[#stack_funcs]
    if fn_info.timeout == 0 or fn_info.start_time == 0 then
        return false
    end
    -- 效验超时
    if os.time() - fn_info.start_time < fn_info.timeout then
        return false
    end
    -- 确定已超时
    fn_info.is_timeout = true
    return true
end

-------------------------------------------------------------------------------------
-- 进入函数
--
-- @local
-------------------------------------------------------------------------------------
function decider.enter_function(fn_info)
    local id = coroutine.running()
    if not this.co_stack_funcs[id] then
        this.co_stack_funcs[id] = {}
    end
    local stack_funcs = this.co_stack_funcs[id]
    table.insert(stack_funcs, fn_info)
    trace.enter_function(stack_funcs, fn_info)
end

-------------------------------------------------------------------------------------
-- 离开函数
--
-- @local
-------------------------------------------------------------------------------------
function decider.leave_function()
    local id = coroutine.running()
    local stack_funcs = this.co_stack_funcs[id]
    local fn_info = stack_funcs[#stack_funcs]
    trace.leave_function(fn_info)
    table.remove(stack_funcs)
end

-------------------------------------------------------------------------------------
-- 轮循入口, 此函数改为从内部使用。
--
-- @local
-------------------------------------------------------------------------------------
function decider.inside_looping()
    local context = this.current_context
    if context.looping then
        context.looping()
    end
end

-------------------------------------------------------------------------------------
-- 进入模块
--
-- @local
-------------------------------------------------------------------------------------
function decider.entry()
    -- 本地化使用
    local context = this.current_context
    -- 记录进入时间
    context.entry_time = os.time()
    -- 记录进入次数
    context.entry_count = (context.entry_count or 0) + 1
    -- 日志跟踪进入
    trace.enter_module(context.MODULE_NAME)
    -- 函数进入前处理
    if context.pre_enter then
        context.pre_enter()
    end
    -- 重置退出标识
    context.should_exit = false
    -- 调用目标函数
    context.entry()
    -- 标识已经退出
    context.should_exit = true
    -- 函数离开处理
    if context.post_enter then
        context.post_enter()
    end
    -- 记录离开时间
    context.leave_time = os.time()
    -- 计算最后一次占用时长
    local last_occupy_duration = context.leave_time - context.entry_time
    -- 保存最后一次时长
    context.last_occupy_duration = last_occupy_duration
    -- 初始化累计时长
    context.total_occupy_duration = context.total_occupy_duration or 0
    -- 更新总时长
    context.total_occupy_duration = context.total_occupy_duration + last_occupy_duration
    -- 模块超时处理
    if context.on_timeout and context.timeout_state then
        context.on_timeout()
    end
    -- 日志跟踪离开
    trace.leave_module()
end

-------------------------------------------------------------------------------------
-- 异常捕获函数
--
-- @local
-- @tparam       any       err              异常信息
-------------------------------------------------------------------------------------
function decider.error_handler(err)
    -- 设置脚本终止标志
    if not settings.continue_on_error then
        set_exit_state(true)
    end
    trace.log_error(debug.traceback('error: ' .. tostring(err), 2))
end

-------------------------------------------------------------------------------------
-- 函数包装器，函数属性对象。
-- @table 		 func_prop
-- @tfield 		 int 	   func_type 		函数类型
-- @tfield 		 function  cond_func        条件函数
-- @tfield 		 number    timeout 		    超时时间

-------------------------------------------------------------------------------------
-- 函数包装器(记录功能)
--
-- 封装过的函数具有日志功能及其它特殊功能。
-- @tparam 		 string    action_name      行为名称
-- @tparam 		 function  action_func      行为函数
-- @tparam[opt]  func_prop func_prop        函数属性
-- @treturn		 function                   被封装好的新函数
-- @see          func_prop
-- @usage
-- local function func() xxxmsg(2, 'Hello World!') end
-- local function cond_func() return true end
-- local func1 = decider.function_wrapper('加法运算1', func)
-- local func2 = decider.function_wrapper('加法运算2', func, {cond_func = cond_func})
-- local func3 = decider.function_wrapper('加法运算3', func, {timeout = 10.5}))
-- local func4 = decider.function_wrapper('加法运算4', func, {func_type = 1})
-------------------------------------------------------------------------------------
function decider.function_wrapper(action_name, action_func, func_prop)
    -- 断言效验
    assert(action_name ~= nil, '行为名称不能为空')
    assert(action_func ~= nil, '行为函数不能为NIL')
    -- 函数缓存key
    local func_key = xxhash('function_wrapper_' .. action_name .. tostring(action_func))
    -- 生成函数
    if not this.wrapped_funcs[func_key] then
        -- 函数信息对象
        local fn_info = {}
        -- 函数属性对象
        local func_prop = func_prop or {}
        -- 行为名称
        fn_info.action_name = action_name
        -- 目标函数
        fn_info.action_func = action_func
        -- 前置条件函数
        fn_info.cond_func = func_prop.cond_func
        -- 超时时间
        fn_info.timeout = func_prop.timeout or 0
        -- 普通函数(0) 行为函数(1)
        fn_info.func_type = func_prop.func_type or 0
        -- 开始时间
        fn_info.start_time = 0
        -- 最后一次调用用时
        fn_info.last_occupy_duration = 0
        -- 是否超时
        fn_info.is_timeout = false
        -- 计算计数器key
        local call_count_key = xxhash('function_wrapper_fn_info_call_count' .. action_name)
        local fail_count_key = xxhash('function_wrapper_fn_info_fail_count' .. action_name)
        -- 运行计次：记录函数被调用的次数。
        fn_info.call_count = get_counter_value(call_count_key)
        fn_info.clear_call_count = function()
            clear_counter(call_count_key)
        end
        -- 失败计次：记录函数调用失败的次数。
        fn_info.fail_count = get_counter_value(fail_count_key)
        fn_info.clear_fail_count = function()
            clear_counter(fail_count_key)
        end
        -- 缓存函数信息
        this.wrapped_info_funcs[func_key] = fn_info
        -- 构建函数
        this.wrapped_funcs[func_key] = function(...)
            -- 效验工作状态
            if not this.is_working(true) then
                return false, 'decider.is_working equal false'
            end
            -- 效验可执行状态
            if not this.should_execute() then
                return false, 'decider.should_execute equal false'
            end
            -- 重置默认返回值
            fn_info.result = { false }
            -- 效验前置条件
            if not fn_info.cond_func or fn_info.cond_func(...) then
                -- 参数列表
                fn_info.params = { ... }
                -- 开始时间
                fn_info.start_time = os.time()
                -- 重置超时
                fn_info.is_timeout = false
                -- 累加运行次数
                fn_info.call_count = increment_counter(call_count_key)
                -- 记录函数进入
                this.enter_function(fn_info)
                -- 调用目标函数
                fn_info.result = { fn_info.action_func(...) }
                -- 记录函数离开
                this.leave_function()
                -- 统计用时
                fn_info.last_occupy_duration = os.time() - fn_info.start_time
                -- 检查函数执行是否成功
                if #fn_info.result > 0 and not fn_info.result[1] then
                    -- 失败计次增加
                    fn_info.fail_count = increment_counter(fail_count_key)
                    if this.current_monitor and settings.halt_on_failure and fn_info.func_type == 1 then
                        local handle_action_failure = this.current_monitor.handle_action_failure
                        xpcall(handle_action_failure, this.error_handler, this.current_context, fn_info)
                    end
                end
            end
            -- 返回结果
            return table.unpack(fn_info.result)
        end
    end
    -- 返回函数
    return this.wrapped_funcs[func_key]
end

-------------------------------------------------------------------------------------
-- 函数包装器(普通函数)
--
-- @tparam 		 string    action_name      行为名称
-- @tparam 		 function  action_func      行为函数
-- @tparam[opt]  function  cond_func        条件函数
-- @treturn 	 function      	            被封装好的新函数
-- @usage
-- local function func() xxxmsg(2, 'Hello World!') end
-- local function cond_func() return true end
-- local func1 = decider.run_normal_wrapper('加法运算1', func)
-- local func2 = decider.run_normal_wrapper('加法运算2', func, cond_func)
-------------------------------------------------------------------------------------
function decider.run_normal_wrapper(action_name, action_func, cond_func)
    return this.function_wrapper(action_name, action_func, { cond_func = cond_func })
end

-------------------------------------------------------------------------------------
-- 函数包装器(带前置条件)
--
-- @tparam 		 string    action_name      行为名称
-- @tparam 		 function  action_func      行为函数
-- @tparam[opt]  function  cond_func        条件函数
-- @treturn 	 function      	            被封装好的新函数
-- @usage
-- local function func() xxxmsg(2, 'Hello World!') end
-- local function cond_func() return true end
-- local func1 = decider.run_condition_wrapper('加法运算1', func)
-- local func2 = decider.run_condition_wrapper('加法运算2', func, cond_func)
-------------------------------------------------------------------------------------
function decider.run_condition_wrapper(action_name, action_func, cond_func)
    return this.function_wrapper(action_name, action_func, { cond_func = cond_func })
end

-------------------------------------------------------------------------------------
-- 函数包装器(带超时功能)
--
-- @tparam 		 string    action_name      行为名称
-- @tparam 		 function  action_func      行为函数
-- @tparam 		 number    timeout  	    超时时间(秒)
-- @tparam[opt]  function  cond_func        条件函数
-- @treturn 	 function      	            被封装好的新函数
-- @usage
-- local function func() xxxmsg(2, 'Hello World!') end
-- local function cond_func() return true end
-- local func1 = decider.run_timeout_wrapper('加法运算1', func, 10.5)
-- local func2 = decider.run_timeout_wrapper('加法运算2', func, 10.5, cond_func)
-------------------------------------------------------------------------------------
function decider.run_timeout_wrapper(action_name, action_func, timeout, cond_func)
    return this.function_wrapper(action_name, action_func, { cond_func = cond_func, timeout = timeout })
end

-------------------------------------------------------------------------------------
-- 函数包装器(行为类函数)
--
-- @tparam 		 string    action_name      行为名称
-- @tparam 		 function  action_func      行为函数
-- @tparam[opt]  function  cond_func        条件函数
-- @treturn 	 function      	            被封装好的新函数
-- @usage
-- local function func() xxxmsg(2, 'Hello World!') end
-- local function cond_func() return true end
-- local func1 = decider.run_action_wrapper('加法运算1', func)
-- local func2 = decider.run_action_wrapper('加法运算2', func, cond_func)
-------------------------------------------------------------------------------------
function decider.run_action_wrapper(action_name, action_func, cond_func)
    return this.function_wrapper(action_name, action_func, { cond_func = cond_func, func_type = 1 })
end

-------------------------------------------------------------------------------------
-- 包装间隔运行
--
-- @tparam 		 string    action_name  	行为名称
-- @tparam 		 function  action_func      行为函数
-- @tparam 	     integer   intv_time        间隔时间
-- @treturn 	 function      	            被封装好的新函数
-- @usage
-- local function func() xxxmsg(2, 'Hello World!') end
-- local func1 = decider.run_interval_wrapper('加法运算1', func， 1000*30)
-------------------------------------------------------------------------------------
function decider.run_interval_wrapper(action_name, action_func, intv_time)
    -- 默认时间间隔设置为0
    intv_time = intv_time or 0

    -- 使用特定的格式构建函数缓存key
    local func_key = xxhash('run_interval_wrapper_' .. action_name .. tostring(action_func))

    -- 只在未缓存过的情况下生成函数
    if not this.wrapped_funcs[func_key] then
        this.wrapped_funcs[func_key] = function(...)
            -- 检查是否应该运行这个函数，基于间隔时间
            if not get_run_signal(action_func, intv_time) then
                return false, 'no run signal' -- 未到运行时间
            end

            -- 调用目标函数，使用另一个包装器增加额外的处理逻辑
            local fn = this.function_wrapper(action_name, action_func)
            return fn(...)
        end
    end

    -- 返回已经包装过的函数
    return this.wrapped_funcs[func_key]
end

-------------------------------------------------------------------------------------
-- 包装只运行一次
--
-- @tparam 		 string    action_name      行为名称
-- @tparam 		 function  action_func      行为函数
-- @treturn 	 function      	            被封装好的新函数
-- @usage
-- local function func() xxxmsg(2, 'Hello World!') end
-- local func1 = decider.run_once_wrapper('加法运算1', func)
-------------------------------------------------------------------------------------
function decider.run_once_wrapper(action_name, action_func)
    return this.run_interval_wrapper(action_name, action_func, 0xFFFFFFFF)
end

-------------------------------------------------------------------------------------
-- 包装运行到条件满足
--
-- @tparam 		 function  action_func      行为函数
-- @tparam 		 function  condition        条件函数
-- @tparam 		 number    timeout  	    超时时间(秒)
-- @treturn 	 function      	            被封装好的新函数
-------------------------------------------------------------------------------------
function decider.run_until_wrapper(action_func, condition, timeout)
    -- 确保 timeout 被设置，并且是一个有效的数字
    assert(type(timeout) == 'number' and timeout > 0, 'Timeout must be a positive number.')

    -- 构造一个唯一的缓存键，基于动作函数的标识
    local func_key = xxhash('run_until_' .. tostring(action_func))

    -- 检查缓存中是否已经存在包装过的函数，不存在则创建
    if not this.wrapped_funcs[func_key] then
        this.wrapped_funcs[func_key] = function(...)
            local start_time = os.time() -- 记录开始时间，用于超时判断
            repeat
                -- 首先检查条件是否已满足，如果满足则立即返回 true
                if condition(...) then
                    return true
                end
                -- 执行目标函数
                action_func(...)
                -- 检查是否超时，若超时则退出循环
            until not this.is_working(true) or os.difftime(os.time(), start_time) > timeout
            -- 如果循环结束还没有满足条件，则返回 false
            return false
        end
    end
    -- 返回缓存中的函数，避免重复创建相同功能的函数
    return this.wrapped_funcs[func_key]
end

-------------------------------------------------------------------------------------
-- 提取公共部分，用于返回错误信息
--
-- 该函数被设计用来统一处理错误退出的情况，提高代码的复用性和可维护性
-- @param message string 错误消息，描述错误的详细信息
-- @return false 固定返回false，表示操作失败
-- @return message string 返回传入的错误消息，用于外部处理或显示
-------------------------------------------------------------------------------------
function decider.error_exit(...)
    return false, table.concat({ ... })
end

-------------------------------------------------------------------------------------
-- 构造成功的返回信息
--
-- 该函数被设计用来统一处理成功完成的情况，提高代码的复用性和可维护性
-- @param ... 可变参数，用于构建成功消息的部分
-- @return true 固定返回true，表示操作成功
-- @return message string 返回构成的成功消息，用于外部处理或显示
-------------------------------------------------------------------------------------
function decider.success_exit(...)
    return true, table.concat({ ... })
end

-------------------------------------------------------------------------------------
-- 缓存函数调用的结果，并在一定时间内返回缓存结果。
--
-- @param func function            要调用的函数。
-- @param time number              结果缓存的有效时间（秒）。
-- @param ... any                  调用函数时传递的参数。
-- @treturn ...                    调用函数的结果，如果目标函数未返回任何值，则抛出错误。
-------------------------------------------------------------------------------------
function decider.cached_function_call(func, time, ...)
    -- 使用具体描述性名称的缓存
    local cache = this.function_execution_cache

    -- 检查缓存中是否有有效的结果，如果在有效期内则返回结果
    if cache[func] and os.difftime(os.time(), cache[func].time) < time then
        return table.unpack(cache[func].result)
    end

    -- 调用目标函数
    local result = { func(...) }
    if #result == 0 then
        error('The function did not return any value. Please check the implementation.')
    end

    -- 缓存函数执行结果和当前时间
    cache[func] = { result = result, time = os.time() }
    return table.unpack(result)
end

------------------------------------------------------------------------------------
-- 将对象转换为字符串
--
-- @local
-- @treturn      string                     模块名称
------------------------------------------------------------------------------------
function decider.__tostring()
    return this.MODULE_NAME
end

------------------------------------------------------------------------------------
-- 防止动态修改(this.READ_ONLY值控制)
--
-- @local
-- @tparam       table     t                被修改的表
-- @tparam       any       k                要修改的键
-- @tparam       any       v                要修改的值
------------------------------------------------------------------------------------
function decider.__newindex(t, k, v)
    if this.READ_ONLY then
        error('attempt to modify read-only table')
        return
    end
    rawset(t, k, v)
end

------------------------------------------------------------------------------------
-- 设置decider的__index元方法指向自身
--
-- @local
------------------------------------------------------------------------------------
decider.__index = decider

------------------------------------------------------------------------------------
-- 创建一个新的实例
--
-- @local
-- @tparam       table     args             可选参数，用于初始化新实例
-- @treturn      table                      新创建的实例
------------------------------------------------------------------------------------
function decider:new(args)
    local new = {}
    -- 预载函数(重载脚本时)
    if this.super_preload then
        this.super_preload()
    end
    -- 将args中的键值对复制到新实例中
    if args then
        for key, val in pairs(args) do
            new[key] = val
        end
    end
    -- 设置元表
    return setmetatable(new, decider)
end

-------------------------------------------------------------------------------------
-- 返回实例对象
-------------------------------------------------------------------------------------
return decider:new()

-------------------------------------------------------------------------------------
